// filesys.cc 
//	Routines to manage the overall operation of the file system.
//	Implements routines to map from textual file names to files.
//
//	Each file in the file system has:
//	   A file header, stored in a sector on disk 
//		(the size of the file header data structure is arranged
//		to be precisely the size of 1 disk sector)
//	   A number of data blocks
//	   An entry in the file system directory
//
// 	The file system consists of several data structures:
//	   A bitmap of free disk sectors (cf. bitmap.h)
//	   A directory of file names and file headers
//
//      Both the bitmap and the directory are represented as normal
//	files.  Their file headers are located in specific sectors
//	(sector 0 and sector 1), so that the file system can find them 
//	on bootup.
//
//	The file system assumes that the bitmap and directory files are
//	kept "open" continuously while Nachos is running.
//
//	For those operations (such as Create, Remove) that modify the
//	directory and/or bitmap, if the operation succeeds, the changes
//	are written immediately back to disk (the two files are kept
//	open during all this time).  If the operation fails, and we have
//	modified part of the directory and/or bitmap, we simply discard
//	the changed version, without writing it back to disk.
//
// 	Our implementation at this point has the following restrictions:
//
//	   there is no synchronization for concurrent accesses
//	   files have a fixed size, set when the file is created
//	   files cannot be bigger than about 3KB in size
//	   there is no hierarchical directory structure, and only a limited
//	     number of files can be added to the system
//	   there is no attempt to make the system robust to failures
//	    (if Nachos exits in the middle of an operation that modifies
//	    the file system, it may corrupt the disk)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.
#ifndef FILESYS_STUB

#include "filesys.h"
#include "copyright.h"
#include "debug.h"
#include "disk.h"
#include "pbitmap.h"
#include "filehdr.h"
#include "sysdep.h"

// Sectors containing the file headers for the bitmap of free sectors,
// and the directory of files.  These file headers are placed in well-known 
// sectors, so that they can be located on boot-up.
#define FreeMapSector 		0
#define DirectorySector 	1

// Initial file sizes for the bitmap and directory; until the file system
// supports extensible files, the directory size sets the maximum number 
// of files that can be loaded onto the disk.
#define FreeMapFileSize 	(NumSectors / BitsInByte)
#define NumDirEntries 		10
#define DirectoryFileSize 	(sizeof(DirectoryEntry) * NumDirEntries)

//----------------------------------------------------------------------
// FileSystem::FileSystem
// 	Initialize the file system.  If format = TRUE, the disk has
//	nothing on it, and we need to initialize the disk to contain
//	an empty directory, and a bitmap of free sectors (with almost but
//	not all of the sectors marked as free).  
//
//	If format = FALSE, we just have to open the files
//	representing the bitmap and the directory.
//
//	"format" -- should we initialize the disk?
//----------------------------------------------------------------------

FileSystem::FileSystem(bool format)
{ 
    DEBUG(dbgFile, "Initializing the file system.");
    lstWorkPath = new list<string>();

    if (format) {
        PersistentBitmap *freeMap = new PersistentBitmap(NumSectors);
        Directory *directory = new Directory(NumDirEntries);
	FileHeader *mapHdr = new FileHeader;
	FileHeader *dirHdr = new FileHeader;

        DEBUG(dbgFile, "Formatting the file system.");

    // First, allocate space for FileHeaders for the directory and bitmap
    // (make sure no one else grabs these!)
	freeMap->Mark(FreeMapSector);	    
	freeMap->Mark(DirectorySector);

    // Second, allocate space for the data blocks containing the contents
    // of the directory and bitmap files.  There better be enough space!

	ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize));
	ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize));

    // Flush the bitmap and directory FileHeaders back to disk
    // We need to do this before we can "Open" the file, since open
    // reads the file header off of disk (and currently the disk has garbage
    // on it!).

        DEBUG(dbgFile, "Writing headers back to disk.");
	mapHdr->WriteBack(FreeMapSector);    
	dirHdr->WriteBack(DirectorySector);

    // OK to open the bitmap and directory files now
    // The file system operations assume these two files are left open
    // while Nachos is running.

        freeMapFile = new OpenFile(FreeMapSector);
        rootDirFile = new OpenFile(DirectorySector);
     
    // Once we have the files "open", we can write the initial version
    // of each file back to disk.  The directory at this point is completely
    // empty; but the bitmap has been changed to reflect the fact that
    // sectors on the disk have been allocated for the file headers and
    // to hold the file data for the directory and bitmap.

        DEBUG(dbgFile, "Writing bitmap and directory back to disk.");
	freeMap->WriteBack(freeMapFile);	 // flush changes to disk
	directory->WriteBack(rootDirFile);

	if (debug->IsEnabled('f')) {
	    freeMap->Print();
	    directory->Print();
        }
        delete freeMap; 
	delete directory; 
	delete mapHdr; 
	delete dirHdr;
    } else {
    // if we are not formatting the disk, just open the files representing
    // the bitmap and directory; these are left open while Nachos is running
        freeMapFile = new OpenFile(FreeMapSector);
        rootDirFile = new OpenFile(DirectorySector);
    }
}


//----------------------------------------------------------------------
// FileSystem::~FileSystem
// 	Destruct the filesystem
//
//----------------------------------------------------------------------

FileSystem::~FileSystem()
{
    delete rootDirFile;
    delete freeMapFile;
    delete lstWorkPath;
}

void FileSystem::SplitePath( char * szPath, char * szName )
{
    std::string szDirPath = string( szPath );
    size_t pos = szDirPath.find_last_of( "/\\" );

    if ( string::npos == pos ) {
        strcpy( szName, szPath );
        strcpy( szPath, "." );
    } else {
        strcpy( szName, szDirPath.substr(pos+1, szDirPath.size()).c_str() );
        szPath[pos] = '\0';
    }
}

//----------------------------------------------------------------------
// FileSystem::Test
//	Test the newly added directory operations.
//
//----------------------------------------------------------------------

void FileSystem::Install()
{
    string workPath;
    GetCurWkDir( workPath );

    MakeDir( NULL, "bin" );
    MakeDir( NULL, "tmp" );
    GetFile( "../test/shell", "/bin", "shell");
    GetFile( "../test/print.c", "/tmp", "print.c");
    GetFile( "../test/test.txt", "/tmp", "test.txt");
    
    MakeDir( NULL, "usr" );
    MakeDir( "usr", "tyf" );
    MakeDir( "usr", "zwj" );
    MakeDir( "usr", "csl" );
}

//----------------------------------------------------------------------
// FileSystem::MakeDir
// 	Create a directory in the Nachos file system (similar to UNIX mkdir).
//	we have	to give Create the initial size of the file.
//
//	The steps to create a file are:
//	  Make sure the directory doesn't already exist
//        Allocate a sector for the directory file header
// 	  Allocate space on disk for the data blocks for the directory file
//	  Add the name to the parent directory
//	  Store the new directory file header on disk 
//	  Flush the changes to the bitmap and the directory back to disk
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Create fails if:
//   		sub directory is already in directory
//	 	no free space for file header
//	 	no free entry for file in directory
//	 	no free space for data blocks for the file 
//
// 	Note that this implementation assumes there is no concurrent access
//	to the file system!
//
//	"name" -- name of directory to be created
//	"entryCount" -- count of entry of directory to be created
//----------------------------------------------------------------------

bool
FileSystem::MakeDir(char* path, char* name /* , int entryCount */)
{
    Directory *directory;
    PersistentBitmap *freeMap;
    FileHeader *hdr;
    int sector;
    bool success;

    directory = new Directory(NumDirEntries);
    DEBUG(dbgZwj, "MakeDir: Creating directory " << name );
    
    OpenFile * curDirFile = OpenDir( path?path:"." );
    if( NULL == curDirFile )
        return FALSE;
    directory->FetchFrom( curDirFile );

    if (directory->Find(name) != -1)
      success = FALSE;			// file is already in directory
    else {	
        freeMap = new PersistentBitmap(freeMapFile,NumSectors);
        sector = freeMap->FindAndSet();	// find a sector to hold the file header
        DEBUG( dbgZwj, "MakeDir: Set directory header in sector: " << sector );
    	if (sector == -1) 		
            success = FALSE;		// no free block for file header 
        else if (!directory->Add(name, sector, TRUE))
            success = FALSE;	// no space in directory
	else {
    	    hdr = new FileHeader;
	    if (!hdr->Allocate(freeMap, /*entryCount*/NumDirEntries*sizeof(DirectoryEntry)))
            	success = FALSE;	// no space on disk for data
	    else {	
	    	success = TRUE;
		// everthing worked, flush all changes back to disk
    	    	hdr->WriteBack(sector); 		
    	    	directory->WriteBack(curDirFile);
    	    	freeMap->WriteBack(freeMapFile);
	    }
            delete hdr;
	}
        delete freeMap;
    }

    if( curDirFile != rootDirFile ) delete curDirFile;
    delete directory;

    DEBUG( dbgZwj, "MakeDir: Create directory successfully!" );
    return success;
}

//----------------------------------------------------------------------
// FileSystem::ChDir
//	Change the working directory to a sub directory.
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Change fails if:
//      sub directory does not exist in current directory
//	 	no free entry for the directory file
//
// 	Note that this implementation assumes there is no concurrent access
//	to the file system!
//
//	"name" -- name of directory to change to.
//----------------------------------------------------------------------

bool FileSystem::ChDir( char *path )
{
    OpenFile *dirFile = OpenDir(path);
    if( NULL == dirFile )
        return FALSE;
    
    std::list<std::string> lstCurPath;
    if( !FetchPath( path, lstCurPath ) ) 
        return FALSE;

    *lstWorkPath = lstCurPath;    
    return TRUE;
}

//----------------------------------------------------------------------
// FileSystem::GetCurWkDir
//	Get the current working directory .
//
//	"path" -- string to store the current working directory path.
//         -- The path will be in format of absolute path.
//----------------------------------------------------------------------

void FileSystem::GetCurWkDir( std::string& path )
{
    path.clear();
    list<string>::iterator iter = lstWorkPath->begin();
    for( ; iter != lstWorkPath->end(); ++iter )
    {
        path += "/";
        path += *iter;
    }

    if( path.empty() ) path += "/";
    DEBUG( dbgZwj, "GetCurWkDir: the working path is: " << path );

}

//----------------------------------------------------------------------
// FileSystem::OpenDir
//	Open the directory file
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Change fails if:
//      directory does not exist.
//
//	"path" -- path of directory to change to.
//            The path can be absolute or relative
//----------------------------------------------------------------------

OpenFile* FileSystem::OpenDir( const char* path )
{
    DEBUG(dbgZwj, "OpenDir: just begin, path = " << path );
    std::list<std::string> lstCurPath;
    if( !FetchPath( path, lstCurPath ) ) 
        return NULL;
    
    OpenFile *fp = rootDirFile;
    while( !lstCurPath.empty() )
    {
        std::string szEntryName = lstCurPath.front();
        lstCurPath.pop_front();

        Directory *directory = new Directory(NumDirEntries);
        directory->FetchFrom(fp);
        if( fp != rootDirFile ) 
        {
            delete fp;
        }
        
        char entryName[FileNameMaxLen + 1];
        strcpy( entryName, szEntryName.c_str() ); 
        int sector = directory->FindDir( entryName );
        DEBUG(dbgZwj, "OpenDir: entryName = " << entryName
                   << ", get sector num = " << sector);

        if( -1 == sector )
        {
            DEBUG( dbgZwj, "OpenDir: The path is invalid!" );   
            delete directory;
            return NULL;
        }
        
        fp = new OpenFile( sector );    
        delete directory;
    }

    DEBUG(dbgZwj, "OpenDir: end.");
    return fp;
}

//----------------------------------------------------------------------
// FileSystem::FetchPath
//	translate a path to a list of directory names
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Change fails if:
//      format of the path is invalid.
//
//	"szpath" -- path of directory to change to.
//            The path can be absolute or relative
//	"lstCurPath" -- a container to save a list of directory names.
//----------------------------------------------------------------------

bool FileSystem::FetchPath( 
             const char* szPath, 
             std::list<std::string>& lstCurPath )
{
    DEBUG( dbgZwj, "FetchPath: OK into FetchPath" );
    lstCurPath = *(lstWorkPath);
    const char* p = szPath;

    if ( '/' == *p ) 
    {
        lstCurPath.clear();
        ++p;
    }

    std::string szFileName;
    int dotCount = 0;
    while( *p )
    { 
        if( '/' == *p )
        {
            if( !szFileName.empty() )
            {
                lstCurPath.push_back( szFileName );
            }
            szFileName.clear();
            dotCount = 0;
        }
        else if( '.' == *p && szFileName.empty() )
        {
            dotCount++;
            if( dotCount == 2 )
            {
                if( !lstCurPath.empty() ) lstCurPath.pop_back();
                dotCount = 0;
            }
        }
        else
        {
            szFileName.push_back( *p );
            dotCount = 0;
        }

        ++p;
    }
    
    if( !szFileName.empty() )
    {
        lstCurPath.push_back( szFileName );
    } 

    DEBUG( dbgZwj, "FetchPath: your path is:" );
    list<string>::iterator iter = lstCurPath.begin();
    for ( ; iter != lstCurPath.end(); ++iter )
        DEBUG( dbgZwj, "  FetchPath: " << *iter );
    return TRUE;
}
 
//----------------------------------------------------------------------
// FileSystem::Create
// 	Create a file in the Nachos file system (similar to UNIX create).
//	Since we can't increase the size of files dynamically, we have
//	to give Create the initial size of the file.
//
//	The steps to create a file are:
//	  Make sure the file doesn't already exist
//        Allocate a sector for the file header
// 	  Allocate space on disk for the data blocks for the file
//	  Add the name to the directory
//	  Store the new file header on disk 
//	  Flush the changes to the bitmap and the directory back to disk
//
//	Return TRUE if everything goes ok, otherwise, return FALSE.
//
// 	Create fails if:
//   		file is already in directory
//	 	no free space for file header
//	 	no free entry for file in directory
//	 	no free space for data blocks for the file 
//
// 	Note that this implementation assumes there is no concurrent access
//	to the file system!
//
//	"name" -- name of file to be created
//	"initialSize" -- size of file to be created
//----------------------------------------------------------------------

bool
FileSystem::Create(OpenFile* D_directoryFile, char *name, int initialSize)
{
    Directory *directory;
    PersistentBitmap *freeMap;
    FileHeader *hdr;
    int sector;
    bool success;
    OpenFile* dFile = NULL;

    DEBUG(dbgFile, "Creating file " << name << " size " << initialSize);

    directory = new Directory(NumDirEntries);
    if(NULL == D_directoryFile)
    {
        dFile = rootDirFile;
    }
    else
    {
        dFile = D_directoryFile;
    }
    directory->FetchFrom(dFile);
    if (directory->Find(name) != -1)
      success = FALSE;			// file is already in directory
    else {	
        freeMap = new PersistentBitmap(freeMapFile,NumSectors);
        sector = freeMap->FindAndSet();	// find a sector to hold the file header

    	if (sector == -1) 		
            success = FALSE;		// no free block for file header 
        else if (!directory->Add(name, sector, FALSE))
            success = FALSE;	// no space in directory
	else {
    	    hdr = new FileHeader;
	    if (!hdr->Allocate(freeMap, initialSize))
            	success = FALSE;	// no space on disk for data
	    else {	
	    	success = TRUE;
		// everthing worked, flush all changes back to disk
	    	hdr->WriteBack(sector); 
    	    directory->WriteBack(dFile);
	    	freeMap->WriteBack(freeMapFile);
	    }
            delete hdr;
	}
        delete freeMap;
    }
    delete directory;
    return success;
}

bool
FileSystem::Create( char *path, char *name )
{    
    OpenFile * curDirFile = OpenDir( path?path:"." );
    if( NULL == curDirFile )
        return FALSE;
    return Create( curDirFile, name, 0 );
}

//----------------------------------------------------------------------
// FileSystem::Open
// 	Open a file for reading and writing.  
//	To open a file:
//	  Find the location of the file's header, using the directory 
//	  Bring the header into memory
//
//	"name" -- the text name of the file to be opened
//----------------------------------------------------------------------

OpenFile *
FileSystem::Open(char *name)
{ 
    char szPath[64] = {0};
    char szName[10] = {0};
    strcpy( szPath, name );
    SplitePath(szPath, szName);
    
    OpenFile *directory = NULL;
    OpenFile *openFile = NULL;

    DEBUG(dbgFile, "Opening file: path = " << szPath << ", name = " << szName );
    directory = OpenDir( szPath );
    if ( NULL == directory ) {
        DEBUG( dbgZwj, "Open: OpenDir renturn NULL " );
        return NULL;
    }

    openFile = Open( directory, szName );	// name was found in directory 
    if( rootDirFile != directory ) delete directory;
    return openFile;				// return NULL if not found
}

OpenFile *
FileSystem::Open(OpenFile* dirFile, char* name)
{
    Directory *directory = new Directory(NumDirEntries);
    OpenFile *openFile = NULL;
    OpenFile* dFile = NULL;
    int sector;

    DEBUG(dbgFile, "Opening file: " << name);
    if(NULL == dirFile)
    {
        dFile = rootDirFile;
    }
    else
    {
        dFile = dirFile;
    }
    directory->FetchFrom(dFile);
    sector = directory->Find(name); 
    if (sector >= 0) 		
	openFile = new OpenFile(sector);	// name was found in directory 
    delete directory;
    return openFile;				// return NULL if not found
}

//----------------------------------------------------------------------
// FileSystem::Remove
// 	Delete a file from the file system.  This requires:
//	    Remove it from the directory
//	    Delete the space for its header
//	    Delete the space for its data blocks
//	    Write changes to directory, bitmap back to disk
//
//  param:
//  "path" -- the path of the file to be removed
//            NULL for current directory.
//	"name" -- the text name of the file to be removed
//  "isDir" -- the name is a directory or not?
//
//  return value:
//  0  -- success
//  -1 -- no such file or directory 
//  -2 -- not a directory
//  -3 -- not a file
//  -4 -- directory not empty
//
//----------------------------------------------------------------------

int
FileSystem::Remove( char* path, 
                    char *name, 
                    bool isDir/* = false */, 
                    bool isResv /* = false */ )
{ 
    Directory *directory;
    PersistentBitmap *freeMap;
    FileHeader *hdr;
    int sector;

    directory = new Directory(NumDirEntries);
    DEBUG(dbgZwj, "Remove: path = " << path << ", name = " << name
               << ", isDir = " << isDir << ", isResv = " << isResv );
    
    OpenFile * curDirFile = OpenDir( path?path:"." );
    if( NULL == curDirFile )
        return -1;
    directory->FetchFrom( curDirFile );
  
    sector = directory->Find(name);
    if (sector == -1) {
       if( rootDirFile != curDirFile ) delete curDirFile;
       delete directory;
       return -1;			 // file not found 
    }

    if( isDir ) {
        sector = directory->FindDir(name);
        if (sector == -1) {
           DEBUG( dbgZwj, "Remove: is not a directory." );
           if( rootDirFile != curDirFile ) delete curDirFile;
           delete directory;
           return -2;			 // file not found 
        }
    } else {
        sector = directory->FindFile(name);
        if (sector == -1) {
           DEBUG( dbgZwj, "Remove: not a file but a directory." );
           if( rootDirFile != curDirFile ) delete curDirFile;
           delete directory;
           return -3;			 // file not found 
        }
    }

    DirectoryEntry dirent[10];
    int count = 10;
    char szSubPath[64] = { 0 };
    
    strcpy( szSubPath, path );
    strcat( szSubPath, "/" );
    strcat( szSubPath, name );
    bool ret = GetDirEnts( szSubPath, dirent, count);
    //ASSERT(ret);
    
    if( isDir && isResv ) {
        // remove the subdirectories rescuvely
        for( int i = 0; i < count; i++ ) {

            if( dirent[i].isDir ) {
                Remove( szSubPath, dirent[i].name, true, true);                
            }
            else {
                Remove( szSubPath, dirent[i].name, false, false );
            }
        }
    } 
    else if( isDir && !isResv ) {
        // check is the directory is empty?
        if( 0 != count ) { 
            DEBUG( dbgZwj, "Remove: directory not empty." );
            return -4;
        }
    }

    // remove the file(subdirectory)
    hdr = new FileHeader;
    hdr->FetchFrom(sector);
    freeMap = new PersistentBitmap(freeMapFile,NumSectors);

    hdr->Deallocate(freeMap);  		// remove data blocks
    freeMap->Clear(sector);			// remove header block
    directory->Remove(name);

    freeMap->WriteBack(freeMapFile);		// flush to disk
    directory->WriteBack(curDirFile);        // flush to disk
    delete hdr;
    delete directory;
    delete freeMap;
    if( rootDirFile != curDirFile ) delete curDirFile;
    return 0;
} 

//----------------------------------------------------------------------
// FileSystem::Rename
//  Rename a file. This requires:
//      Rename it in the directory
//      WriteBack the directory
//
//      Return TRUE if the file was renamed, otherwise return False
//      Added by chenshuanglin at seedclass
//----------------------------------------------------------------------
bool
FileSystem::Rename(char* path, char *name, char *newname)
{
    int i = 0;
	OpenFile* dFile = OpenDir(path?path:".");
	
    Directory *directory = new Directory(NumDirEntries);
    directory->FetchFrom(dFile);
    i = directory->GetIndex(name);
    if(-1 == i)
    {
		if(dFile != rootDirFile)delete dFile;
        delete directory;
        return FALSE;
    }
    directory->Rename(i, newname);
    directory->WriteBack(dFile);
	if(dFile != rootDirFile)delete dFile;
    delete directory;
    return TRUE;
}

//----------------------------------------------------------------------
// FileSystem::Copy
//  Copy a file to a new file. This requires:
//      open the source file
//      create the destination file at the same size of source file;
//      change the filename in destination directory file
//      copy the fileheader and data
//      write back the directory file
//
//      Return true if copy finished, otherwise return false
//      Added by chenshuanglin at seedclass
//----------------------------------------------------------------------
bool
FileSystem::Copy(char* S_Path,char * S_Name,char* D_Path,char * D_Name)
{
    char datatmp[40] = {0};   //the buffer for caching data
	int position = 0;
    int filesize = 0;       //the size to create a new file
	OpenFile* S_directoryFile = OpenDir(S_Path?S_Path:".");
	OpenFile* D_directoryFile = OpenDir(D_Path?D_Path:".");
    
    if((S_directoryFile == D_directoryFile)&&(S_Name == D_Name)) {       //check whether dfile == sfile
                DEBUG(dbgZwj, "Copy: target name and source name can't be the same" );
		if( rootDirFile != S_directoryFile ) delete S_directoryFile;
		if( rootDirFile != D_directoryFile ) delete D_directoryFile;
        return FALSE;
    }
    
    OpenFile* S_Openfile= Open(S_directoryFile, S_Name);          //open source file
    if(NULL == S_Openfile)
    {
                DEBUG( dbgZwj, "Copy: open file failed.");
		if( rootDirFile != S_directoryFile ) delete S_directoryFile;
		if( rootDirFile != D_directoryFile ) delete D_directoryFile;
        return FALSE;
    }
    
    filesize = S_Openfile->GetFileHdr()->GetnumBytes();        //destination file size

    if((NULL == D_Name) && (S_directoryFile != D_directoryFile))         //copy and not rename
        D_Name = S_Name;
    
    if(FALSE == Create(D_directoryFile, D_Name,filesize)){       //create new file 
		DEBUG(dbgZwj, "Copy: Create file failed." );
                delete S_Openfile; 
		if( rootDirFile != S_directoryFile ) delete S_directoryFile;
		if( rootDirFile != D_directoryFile ) delete D_directoryFile;  
		return FALSE;   
    }
    OpenFile* D_Openfile = Open(D_directoryFile, D_Name);
    if(NULL == D_Openfile)
    {
        DEBUG( dbgZwj, "Copy: Open target file failed." );
        delete S_Openfile; 
		if( rootDirFile != S_directoryFile ) delete S_directoryFile;
		if( rootDirFile != D_directoryFile ) delete D_directoryFile;  
        return FALSE;
    }
	
    /* copy file data */
    do{
    	S_Openfile->ReadAt(datatmp, 40, position);
    	D_Openfile->WriteAt(datatmp, 40, position);
		position += 40;
	}while(position<=S_Openfile->GetFileHdr()->GetnumBytes());
    delete D_Openfile;
    delete S_Openfile;
	if( rootDirFile != S_directoryFile ) delete S_directoryFile;
	if( rootDirFile != D_directoryFile ) delete D_directoryFile;  
    return TRUE;
}

//----------------------------------------------------------------------
// FileSystem::Append
//  Append a file to another file. This requires:
//      remove the source file from the source directory;
//      add the sector number to the destination fileheader;
//      remove the source fileheader;
//      write back the source directoryfile;
//      write back the bitmap
//
//      return true if appending finished, otherwise return false;
//      added by chenshuanglin at seedclass
//----------------------------------------------------------------------
bool
FileSystem::Append(char* S_Path,char * S_Name,char* D_Path,char * D_Name)
{
    int sector , numSectorsTmp;
	int s_position = 0, d_position;
	char datatmp[40] = {0};
	OpenFile* S_directoryFile = OpenDir(S_Path?S_Path:".");
	OpenFile* D_directoryFile = OpenDir(D_Path?D_Path:".");

    OpenFile* D_Openfile = Open(D_directoryFile, D_Name);
    OpenFile* S_Openfile = Open(S_directoryFile, S_Name);
    d_position = D_Openfile->GetFileHdr()->FileLength();
    if((NULL == D_Openfile)||(NULL == S_Openfile))
    {
        if( rootDirFile != S_directoryFile ) delete S_directoryFile;
		if( rootDirFile != D_directoryFile ) delete D_directoryFile;  
        return FALSE;
    }

    Directory* D_Directory = new Directory(NumDirEntries);
    D_Directory->FetchFrom(D_directoryFile);
    sector = D_Directory->Find(D_Name);
    if ( sector < 0 ) return FALSE;

	PersistentBitmap *freeMap = new PersistentBitmap(freeMapFile,NumSectors);

	//append file header first
	numSectorsTmp = divRoundUp(D_Openfile->GetFileHdr()->FileLength(), SectorSize);
	(void)D_Openfile->GetFileHdr()->AppendFileHdr(S_Openfile->GetFileHdr(),freeMap,numSectorsTmp);
	D_Openfile->GetFileHdr()->WriteBack(sector);
	freeMap->WriteBack(freeMapFile);

	//append file data then
	do{
		S_Openfile->ReadAt(datatmp, 40, s_position);
		D_Openfile->WriteAt(datatmp, 40, d_position);
		s_position += 40;
		d_position += 40;
	}while(d_position<=D_Openfile->GetFileHdr()->FileLength());

    delete D_Directory;
    delete freeMap;
    delete S_Openfile;
    delete D_Openfile;
	if( rootDirFile != S_directoryFile ) delete S_directoryFile;
	if( rootDirFile != D_directoryFile ) delete D_directoryFile;  
    return TRUE;
}


//----------------------------------------------------------------------
// FileSystem::List
// 	List all the files in the file system directory.
//----------------------------------------------------------------------

void
FileSystem::List()
{
    Directory *directory = new Directory(NumDirEntries);

    directory->FetchFrom(rootDirFile);
    directory->List();
    delete directory;
}

//----------------------------------------------------------------------
// FileSystem::Print
// 	Print everything about the file system:
//	  the contents of the bitmap
//	  the contents of the directory
//	  for each file in the directory,
//	      the contents of the file header
//	      the data in the file
//----------------------------------------------------------------------

void
FileSystem::Print()
{
    FileHeader *bitHdr = new FileHeader;
    FileHeader *dirHdr = new FileHeader;
    PersistentBitmap *freeMap = new PersistentBitmap(freeMapFile,NumSectors);
    Directory *directory = new Directory(NumDirEntries);

    printf("Bit map file header:\n");
    bitHdr->FetchFrom(FreeMapSector);
    bitHdr->Print();

    printf("Directory file header:\n");
    dirHdr->FetchFrom(DirectorySector);
    dirHdr->Print();

    freeMap->Print();

    directory->FetchFrom(rootDirFile);
    directory->Print();

    delete bitHdr;
    delete dirHdr;
    delete freeMap;
    delete directory;
} 

bool FileSystem::GetDirEnts( 
    char* path, DirectoryEntry * ents, int & cnt )
{
    OpenFile * dirFile = OpenDir(path);
    if( NULL == dirFile ) {
        cnt = 0;
        return FALSE;
    }

    Directory* directory = new Directory(NumDirEntries );
    directory->FetchFrom( dirFile );
    bool ret = directory->GetEntries( ents, cnt );

    if ( rootDirFile != dirFile ) delete dirFile;
    delete directory;
    return TRUE;
}

bool FileSystem::List( std::string & path, bool recv )
{
    Directory *directory;
    PersistentBitmap *freeMap;
    FileHeader *hdr;

    directory = new Directory(NumDirEntries);
    DEBUG(dbgZwj, "List: path = " << path );
    
    OpenFile * curDirFile = OpenDir( path.c_str() );
    if( NULL == curDirFile )
        return FALSE;
    directory->FetchFrom( curDirFile );

    list<std::string> lstCurPath;
    FetchPath(path.c_str(), lstCurPath);
    list<std::string>::iterator iter = lstCurPath.begin();
    for( ; iter != lstCurPath.end(); ++iter ) {
        cout << "/" << *iter ;
    }

    if ( !lstCurPath.empty() )
        cout << ":" << endl;
    else 
        cout << "/:" << endl ;
    directory->List();

    DirectoryEntry ents[10];
    int cnt = 10;
    directory->GetEntries( ents, cnt );

    if ( 0 != cnt  )
        cout << endl;

    if( recv) {
       if ( 0 == cnt )
            return true;

        for ( int i = 0; i < cnt; i++ ) {
            if( !ents[i].isDir )
                continue;
            std::string subPath = path + "/" + ents[i].name;
            List( subPath, recv );
        }
        
    }
    
    return TRUE;
}

bool FileSystem::CatFile( char * path, char * name ) {
 
	OpenFile* dirFile = OpenDir(path?path:".");
    OpenFile* file= Open(dirFile, name);
    if( NULL == file ) {
        DEBUG( dbgZwj, "CatFile: open file failed.");
        return FALSE;
    }

    int amountRead, TransferSize = 40;
    char *buffer;
    buffer = new char[TransferSize];
    
    while ((amountRead = file->Read(buffer, TransferSize)) > 0)
        for (int i = 0; i < amountRead; i++)
            printf("%c", buffer[i]);
        
    delete [] buffer;
    delete file;          
    if( rootDirFile != dirFile ) 
        delete dirFile;
    return TRUE;
}

bool FileSystem::GetFile( char* fromPath, char* toPath, char* toName)
{
    int fd;
    OpenFile* openFile;
    int amountRead, fileLength;
    char *buffer;

// Open UNIX file
    if ((fd = OpenForReadWrite(fromPath,FALSE)) < 0) {       
        printf("Copy: couldn't open input file %s\n", fromPath);
        return FALSE;
    }

// Figure out length of UNIX file
    Lseek(fd, 0, 2);            
    fileLength = Tell(fd);
    Lseek(fd, 0, 0);

// Create a Nachos file of the same length
    DEBUG(dbgZwj, "Copying file " << fromPath << " of size " << fileLength 
              <<  " to file " << toPath << "/" << toName );
    OpenFile* dirFile = OpenDir(toPath?toPath:".");

    if (!Create(dirFile, toName, fileLength)) {   // Create Nachos file
        printf("Copy: couldn't create output file %s\n", toPath);
        Close(fd);
        return FALSE;
    }
    
    OpenFile* file= Open(dirFile, toName);
    ASSERT(file != NULL);
    
// Copy the data in TransferSize chunks
    int TransferSize = 128;
    buffer = new char[TransferSize];
    while ((amountRead=ReadPartial(fd, buffer, sizeof(char)*TransferSize)) > 0)
        file->Write(buffer, amountRead);    
    delete [] buffer;

// Close the UNIX and the Nachos files
    if( rootDirFile != dirFile )  delete dirFile;
    delete file;
    Close(fd);

    return true;
}


#endif // FILESYS_STUB
